package lfp.engine.efop;

import lfp.engine.IForecastModel;
import hhh.opt.matrix.ColumnVector;
import hhh.opt.matrix.GeneralMatrix;
import hhh.opt.matrix.MatrixOp;

/**
 * Created by IntelliJ IDEA.
 * User: zhang siyuan
 * Date: 2008-10-17
 * Time: 10:44:33
 */
public class ArmaxModel implements IForecastModel {
    private static final int NUMOFFREQUENCE = 10;
    private int n = 5;
    private int m = 4;
    private int r = 3;

    public double[] forecast(double[] series, int series_Period, int forelength) {
        int len = series.length;
        double[] u = sinInput(len + forelength, series_Period, NUMOFFREQUENCE);
        double[] y = new double[len + forelength];
        System.arraycopy(series, 0, y, 0, len);
        double[] u1 = new double[len];
        double[] u2 = new double[forelength];
        System.arraycopy(u, 0, u1, 0, len);
        System.arraycopy(u, len, u2, 0, forelength);
        ParaEstimate paraEstimate = new ParaEstimate(u1, series);
        GeneralMatrix pTheta = paraEstimate.estimate(n, m, r);
        GeneralMatrix fai = new GeneralMatrix(1, n + m);
        double value;
        for (int i = 0; i < forelength; i++) {
            for (int j = 0; j < n; j++) fai.setValue(0, j, -y[len -1 + i - j]);
            for (int j = 0; j < m; j++) fai.setValue(0, n+j, u[len -1 + i - j]);
            value = MatrixOp.multiply(fai, pTheta).getValue(0, 0);
            y[len + i] = value;
        }
        double[] result = new double[forelength];
        System.arraycopy(y, len, result, 0, forelength);
        return result;
    }

    private double[] sinInput(int nLen, int seriesPeriod, int nf) {
        GeneralMatrix gm1 = new GeneralMatrix(seriesPeriod, nf);
        for (int k = 0; k < seriesPeriod; k++) {
            for (int j = 0; j < nf; j++) {
                gm1.setValue(k, j, Math.sin(((k+1) * (j+1) * 2 * Math.PI) / seriesPeriod));
            }
        }
        GeneralMatrix gm2 = MatrixOp.ones(nf, 1);
        GeneralMatrix gm3 = MatrixOp.multiply(gm1, gm2);
        double[] u = new double[nLen];
        for (int k = 0; k < nLen; k++) {
            u[k] = gm3.getValue(k % seriesPeriod, 0);
        }
        return u;
    }
}
